package my.org.swing.date;

import my.org.swing.animate.LineBorder;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.event.MouseEvent;
import java.time.LocalDate;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.MouseInputListener;
import javax.swing.table.TableCellRenderer;

public class DateTable extends JTable implements MouseInputListener {

    private int cellSize;

    private int onRow = -1;

    private int onColumn = -1;

    private int selectedRow = -1;

    private int selectedCol = -1;

    private LocalDate calendar;

    private int mode;

    public DateTable(int mode, LocalDate calendar) {
        super(new DateModel(mode, calendar));

        this.mode = mode;
        this.calendar = calendar;

        if (mode == 2) {
            cellSize = DateTheme.smallCellSize;
        } else {
            cellSize = DateTheme.bigCellSize;
        }

        setBackground(DateTheme.backColor);
        setGridColor(DateTheme.backColor);
        setCellSelectionEnabled(true);
        setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        setRowHeight(cellSize);

        init();
    }

    private void init() {
        addMouseListener(this);
        addMouseMotionListener(this);
        setDefaultRenderer(getColumnClass(0), new TableCellRenderer() {

            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
                JLabel cell = new JLabel(value.toString());
                cell.setHorizontalAlignment(JLabel.CENTER);
                cell.setOpaque(true);
                cell.setBackground(DateTheme.backColor);
                cell.setForeground(DateTheme.currentPageForeground);
                cell.setFont(DateTheme.currentPageCellFont);

                CellValue cellValue = (CellValue) value;
                Color color = DateTheme.cellMarkedBackground;
                if (isCurrentTime(cellValue)) {
                    cell.setBackground(color);
                    cell.setForeground(DateTheme.cellMarkedForeground);
                } else if (!isCurrentPageCell(cellValue)) {
                    cell.setFont(DateTheme.otherPageCellFont);
                    cell.setForeground(DateTheme.otherPageForeground);
                }

                if (isSelected(row, col)) {
                    if (isCurrentTime(cellValue)) {
                        if (isRollover(row, col)) {
                            color = DateTheme.cellRolloverBorderColor;
                        }
                        cell.setBorder(BorderFactory.createCompoundBorder(new LineBorder(color, 3), new LineBorder(DateTheme.cellMarkedLineColor, 1.7)));
                    } else {
                        cell.setBorder(new LineBorder(DateTheme.cellSelectedBorderColor, 2));
                    }
                } else if (isRollover(row, col)) {
                    cell.setBorder(new LineBorder(DateTheme.cellRolloverBorderColor, 2));
                } else if (!isCurrentTime(cellValue) && Math.abs(row - onRow) + Math.abs(col - onColumn) == 1) {
                    Color color1 = DateTheme.cellRolloverStartColor;
                    Color color2 = DateTheme.cellRolloverEndColor;
                    int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
                    if (atRolloverTop(row, col)) {
                        y1 = cellSize;
                    } else if (atRolloverBottom(row, col)) {
                        y2 = cellSize;
                    } else if (atRolloverLeft(row, col)) {
                        x1 = cellSize;
                    } else if (atRolloverRight(row, col)) {
                        x2 = cellSize;
                    }
                    cell.setBorder(new LineBorder(new GradientPaint(x1, y1, color1, x2, y2, color2), 2));

                }

                return cell;
            }

            private boolean isSelected(int row, int col) {
                return row == selectedRow && col == selectedCol;
            }

            private boolean isRollover(int row, int col) {
                return row == onRow && col == onColumn;
            }

            private boolean atRolloverLeft(int row, int col) {
                return row == onRow && col == onColumn - 1;
            }

            private boolean atRolloverRight(int row, int col) {
                return row == onRow && col == onColumn + 1;
            }

            private boolean atRolloverTop(int row, int col) {
                return row == onRow - 1 && col == onColumn;
            }

            private boolean atRolloverBottom(int row, int col) {
                return row == onRow + 1 && col == onColumn;
            }

            private boolean isCurrentTime(CellValue value) {
                if (mode == 2) {
                    return LocalDate.now().equals(value.toLocalDate());
                }

                if (mode == 1) {
                    return LocalDate.now().getYear() == value.getYear() && LocalDate.now().getMonthValue() == value.getMonth();
                }

                return LocalDate.now().getYear() == value.getYear();
            }

            private boolean isCurrentPageCell(CellValue value) {
                if (mode == 2) {
                    return value.getMonth() == calendar.getMonthValue();
                }

                int year = value.getYear();
                if (mode == 1) {
                    return year == calendar.getYear();
                }

                int currentYear = calendar.getYear();
                int offset = currentYear % 10;
                int firstYear = currentYear - offset;

                return year >= firstYear && year <= firstYear + 9;
            }
        });
    }

    public int getCellSize() {
        return cellSize;
    }

    public LocalDate changeOffset(int offset) {
        DateModel model = (DateModel) getModel();
        model.changeOffset(offset);
        model.fireTableDataChanged();
        calendar = getShowingDate();
        return calendar;
    }

    public LocalDate getShowingDate() {
        return ((DateModel) getModel()).getShowingDate();
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON1) {
            int row = rowAtPoint(e.getPoint());
            int col = columnAtPoint(e.getPoint());

            if (row >= 0 && col >= 0 && (selectedRow != row || selectedCol != col)) {
                selectedRow = row;
                selectedCol = col;
            } else {
                selectedRow = -1;
                selectedCol = -1;
            }

            repaint();
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {
        onRow = -1;
        onColumn = -1;
        repaint();
    }

    @Override
    public void mouseDragged(MouseEvent e) {

    }

    @Override
    public void mouseMoved(MouseEvent e) {
        onRow = rowAtPoint(e.getPoint());
        onColumn = columnAtPoint(e.getPoint());
        repaint();
    }

    public void clearListener() {
        removeMouseListener(this);
        removeMouseMotionListener(this);
    }
}
